//
//  ActivityIndicator.swift
//  XNetwork
//
//  Created by x on 2024/8/18.
//
#if !RX_NO_MODULE
import RxSwift
import RxCocoa
#endif

/**
 1. ActivityIndicator初始化，_relay个数为0，状态为false
 2. 某个信号源A调用trackActivity方法，准备监测
 3. ActivityIndicator调用trackActivityOfObservable方法，使用using方法，先对_relay+1，然后创建ActivityToken，ActivityToken创建时，持有了A，并且使用ActivityIndicator的-1方法作为闭包参数创建Disposables对象。
 4. 创建ActivityToken完成，接着using调用observableFactory方法，把token持有的resource（信号源A）作为结果返回，因此信号源A通过调用trackActivity方法之后，返回的信号源的信号量与A一致，只不过经过了层层封装
 5. 此时ActivityIndicator的_relay个数为1，状态为true
 6. A完成，订阅释放
 7.. ActivityToken触发dispose方法，调用ActivityIndicator的-1方法，然后ActivityToken释放
 8. 此时ActivityIndicator的_relay个数为0，状态为false
 */
private struct ActivityToken<E>: ObservableConvertibleType, Disposable {
    private let _source: Observable<E>
    private let _dispose: Cancelable

    init(source: Observable<E>, disposeAction: @escaping () -> Void) {
        _source = source
        _dispose = Disposables.create(with: disposeAction)
    }
    func dispose() {
        _dispose.dispose()
    }
    func asObservable() -> Observable<E> {
        return _source
    }
}

public class ActivityIndicator: SharedSequenceConvertibleType {
    public typealias Element = Bool
    public typealias SharingStrategy = DriverSharingStrategy

    private let _lock = NSRecursiveLock()
    private let _relay = BehaviorRelay(value: 0)
    private let _loading: SharedSequence<SharingStrategy, Bool>

    public init() {
        _loading = _relay.asDriver().map { $0 > 0}.distinctUntilChanged()
    }

    fileprivate func trackActivityOfObservable<Source: ObservableConvertibleType>(_ source: Source) -> Observable<Source.Element> {
        return Observable.using {
            () -> ActivityToken<Source.Element> in
            self.increment()
            return ActivityToken(source: source.asObservable(), disposeAction: self.decrement)
        } observableFactory: { value in
            return value.asObservable()
        }
    }

    private func increment() {
        _lock.lock()
        _relay.accept(_relay.value + 1)
        _lock.unlock()
    }

    private func decrement() {
        _lock.lock()
        _relay.accept(_relay.value - 1)
        _lock.unlock()
    }

    public func asSharedSequence() -> SharedSequence<SharingStrategy, Element> {
        return _loading
    }
}

extension ObservableConvertibleType {
    public func trackActivity(_ activityIndicator: ActivityIndicator) -> Observable<Element> {
        return activityIndicator.trackActivityOfObservable(self)
    }
}

